feat: add generic custom data source widget#610
feat: add generic custom data source widget#610RC1140 wants to merge 1 commit intoimmichFrame:mainfrom
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (15)
✅ Files skipped from review due to trivial changes (8)
🚧 Files skipped from review as they are similar to previous changes (5)
📝 WalkthroughWalkthroughThis PR adds a custom widget feature that enables users to display data fetched from external HTTP endpoints. It includes service-layer caching, API endpoints, configuration management, and a frontend Svelte component for rendering the widgets on the home page. Changes
Sequence DiagramsequenceDiagram
participant FrontEnd as Frontend Component
participant API as CustomWidget<br/>Controller
participant Service as CustomWidget<br/>Service
participant Cache as Memory Cache
participant External as External<br/>HTTP Source
FrontEnd->>API: GET /api/CustomWidget<br/>(clientIdentifier)
API->>Service: GetCustomWidgetData()
loop For each CustomWidgetSource
Service->>Cache: Check cache<br/>(key: customwidget_{url})
alt Cache Hit
Cache-->>Service: Return CustomWidgetData
else Cache Miss
Service->>External: GET {source.Url}
External-->>Service: JSON Response
Service->>Service: Deserialize JSON<br/>→ CustomWidgetData
Service->>Cache: Store with TTL<br/>(RefreshMinutes)
end
alt Success
Service->>Service: Add to results
else Error
Service->>Service: Log error,<br/>continue
end
end
Service-->>API: List<CustomWidgetData>
API-->>FrontEnd: HTTP 200 + Data
FrontEnd->>FrontEnd: Update widgetData<br/>Schedule next refresh
FrontEnd->>FrontEnd: Re-render widgets
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested Labels
Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Add a configurable widget that fetches structured data from user-specified URLs and displays it on the photo frame. Supports multiple data sources, each with independent cache TTL via CustomWidgetSources config. Backend: - CustomWidgetSourceConfig model for per-source URL + refresh interval - CustomWidgetData/CustomWidgetItem response models - ICustomWidgetService interface + CustomWidgetService with per-source caching - CustomWidgetController proxying data (keeps URLs server-side) - Settings: ShowCustomWidget, CustomWidgetSources, CustomWidgetPosition - ClientSettingsDto exposes ShowCustomWidget and CustomWidgetPosition Frontend: - custom-widget.svelte component with configurable corner positioning - Integrated into home-page.svelte with conditional render - Regenerated oazapfts API client with new types
|
Do you have any example sources by chance to test? |
|
@JW-CH Sorry I built that out myself will add some screenshots in a bit. This is what it looks like when rendered using the webview As mentioned the API endpoint I hit to get my strength data is a custom service and not really anything public, but returning the data in the structure below works for me atleast The idea here is that you have a title and then any number of rows returned which then gets rendered. You can add multiple sources if your data is not centralized |
50b48a5 to
8066eee
Compare
|
Dont know how much extra work this would be but you can test it with a small python server like this Paste that in a console and hit localhost:8080 |
JW-CH
left a comment
There was a problem hiding this comment.
Hey, thank you very much for your contribution! Really good implementation following the standards of the existing services.
I did an initial review with some first comments, I will also add another comment on the 'structure' of the expected Api-Data.
| private readonly ILogger<AssetController> _logger; | ||
| private readonly ICustomWidgetService _customWidgetService; | ||
|
|
||
| public CustomWidgetController(ILogger<AssetController> logger, ICustomWidgetService customWidgetService) |
There was a problem hiding this comment.
Wrong Logger-Type (AssetController)
| @@ -0,0 +1,6 @@ | |||
| using ImmichFrame.Core.Models; | |||
|
|
|||
| using ImmichFrame.Core.Models; | ||
| using Microsoft.Extensions.Caching.Memory; | ||
| using Microsoft.Extensions.Logging; | ||
|
|
| [ApiController] | ||
| [Route("api/[controller]")] | ||
| [Authorize] | ||
| public class CustomWidgetController : ControllerBase |
There was a problem hiding this comment.
I'd rename the controller to something like 'WidgetController' - Maybe in the Future there could be more Widgets maybe?
| } | ||
|
|
||
| [HttpGet(Name = "GetCustomWidgetData")] | ||
| public async Task<List<CustomWidgetData>> GetCustomWidgetData(string clientIdentifier = "") |
| private readonly IGeneralSettings _settings; | ||
| private readonly ILogger<CustomWidgetService> _logger; | ||
| private readonly IHttpClientFactory _httpClientFactory; | ||
| private readonly IMemoryCache _cache = new MemoryCache(new MemoryCacheOptions()); |
There was a problem hiding this comment.
Could you use the ApiCache here, see OpenWeatherMapService as reference
|
I did a quick Claude research - posting the response after. I quite like the approach claude suggests and checked if this would be possible. Let me know what you think: Clause answer:The current design requires every external API to already return data in the A more open approach would be to add JSONPath mapping + header support directly to A Home Assistant config would then look like this — no proxy needed: If none of the path fields are set, the service falls back to deserializing the response as The main trade-off is a JSONPath dependency (e.g. |
|
One thing the JSONPath mapping doesn't yet solve: selective filtering of items from an array response. For example, querying Home Assistant's A small addition to public List<string>? Include { get; set; } // only render items whose resolved label matchesConfig example: {
"Url": "http://homeassistant:8123/api/states?domain=sensor",
"Headers": { "Authorization": "Bearer <token>" },
"Title": "Sensors",
"ItemsPath": "$",
"LabelPath": "$.attributes.friendly_name",
"ValuePath": "$.state",
"SecondaryPath": "$.attributes.unit_of_measurement",
"Include": ["Living Room Temp", "Outdoor Humidity"]
}The service would filter the resolved items against the Single-entity endpoints are also worth considering. HA exposes individual entities directly:
This returns a single object, not an array, so {
"Url": "http://homeassistant:8123/api/states/sensor.ikea_inspelning_livingroom_power",
"Headers": { "Authorization": "Bearer <token>" },
"Title": "Office",
"LabelPath": "$.attributes.friendly_name",
"ValuePath": "$.state",
"SecondaryPath": "$.attributes.unit_of_measurement"
}This keeps the config intuitive: |

Summary
Adds a configurable widget that fetches structured data from user-specified URLs and displays it on the photo frame. This enables displaying arbitrary external data (fitness stats, Home Assistant sensors, weather from custom sources, etc.) alongside photos.
/api/CustomWidget) — only display config is exposed to the clientConfiguration
Expected response format from data sources
{ "title": "My Stats", "items": [ { "label": "Metric A", "value": "42", "secondary": "optional detail" }, { "label": "Metric B", "value": "100" } ] }Changes
Backend (C# / ASP.NET Core):
CustomWidgetSourceConfigmodel for per-source URL + refresh intervalCustomWidgetData/CustomWidgetItemresponse modelsICustomWidgetService+CustomWidgetServicewith per-source cachingCustomWidgetController— proxies data (keeps URLs server-side)ShowCustomWidget,CustomWidgetSources,CustomWidgetPositionClientSettingsDtoexposes display config onlyFrontend (Svelte 5):
custom-widget.sveltecomponent with configurable corner positioninghome-page.sveltewith conditional renderingTest plan
ShowCustomWidget: falseSummary by CodeRabbit